---
sidebar_position: 3
---

import { longTree } from 'demodata';
import { PropTable } from '../../src/components/PropTable';

# Controlled Environment

A controlled environment provides more flexibility and more possibilities for customization, but
you need to implement your own logic for managing and retaining the view state of each tree, i.e.
which items are expanded, selected, focused etc.

Furthermore, you need to provide all items directly and, if not all items are available at the start,
implement custom logic to asynchronously load items if they need to be displayed.

```jsx live
function App() {
  return (
    <ControlledTreeEnvironment items={longTree.items} getItemTitle={item => item.data} viewState={{}}>
      <Tree treeId="tree-1" rootItem="root" treeLabel="Tree Example" />
    </ControlledTreeEnvironment>
  );
}
```

As you can see from the example, you can focus the tree and even search for items, but not change
the focused or selected item.

## Managing the view state of the tree

To implement this, provide an implementation for the [TreeChangeHandlers](/docs/api/interfaces/TreeChangeHandlers)
interface and provide it as spreaded props to the controlled environment, then provide a
[viewState](/docs/api/interfaces/TreeViewState) prop that defines the visual state of each tree by providing
a [individual viewState](/docs/api/modules#IndividualTreeViewState) for every tree in your environment.

```jsx live
function App() {
  const [focusedItem, setFocusedItem] = useState();
  const [expandedItems, setExpandedItems] = useState([]);
  const [selectedItems, setSelectedItems] = useState([]);
  return (
    <ControlledTreeEnvironment
      items={longTree.items}
      getItemTitle={item => item.data}
      viewState={{
        ['tree-2']: {
          focusedItem,
          expandedItems,
          selectedItems,
        },
      }}
      onFocusItem={item => setFocusedItem(item.index)}
      onExpandItem={item => setExpandedItems([...expandedItems, item.index])}
      onCollapseItem={item =>
        setExpandedItems(expandedItems.filter(expandedItemIndex => expandedItemIndex !== item.index))
      }
      onSelectItems={items => setSelectedItems(items)}
    >
      <Tree treeId="tree-2" rootItem="root" treeLabel="Tree Example" />
    </ControlledTreeEnvironment>
  );
}
```

## Lazy loading items

As with the uncontrolled environment, you can provide an incomplete tree structure with
missing items that are referenced in other items as children. The `onMissingItems` handler
will be invoked if an item is expanded whose children are not yet loaded, so you can
implement logic to load the items if that handler is invoked, and provide them alongside existing
children in the next render iteration.

```jsx live
function App() {
  const [focusedItem, setFocusedItem] = useState();
  const [expandedItems, setExpandedItems] = useState([]);
  return (
    <ControlledTreeEnvironment
      items={{
        root: {
          index: 'root',
          canMove: true,
          isFolder: true,
          children: ['child1'],
          data: 'Root item',
          canRename: true,
        },
        child1: {
          index: 'child1',
          canMove: true,
          isFolder: true,
          children: ['child2'],
          data: 'Child item',
          canRename: true,
        },
      }}
      getItemTitle={item => item.data}
      viewState={{
        ['tree-3']: {
          focusedItem,
          expandedItems,
        },
      }}
      onFocusItem={item => setFocusedItem(item.index)}
      onExpandItem={item => setExpandedItems([...expandedItems, item.index])}
      onMissingItems={items => alert(`We should now load the items ${items.join(', ')}...`)}
    >
      <Tree treeId="tree-3" rootItem="root" treeLabel="Tree Example" />
    </ControlledTreeEnvironment>
  );
}
```

## Component Props

The props for the `ControlledTreeEnvironment` are as follows:

<PropTable name="ControlledTreeEnvironment" />
